package tutor;

import static org.lwjgl.opengl.GL11.*;

import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

public class Camera {
	// Field Of View
	private float fov;
	// Aspect Ratio
	private float aspect;
	// Near Plane
	private float zNear;
	// Far Plane
	private float zFar;

	// Projection matrix
	private Matrix4f projection;
	// View matrix
	private Matrix4f view;

	// Camera position
	private Vector3f position;
	// Camera rotation
	private Vector3f rotation;

	// Vectors for axes
	private Vector3f xAxis, yAxis, zAxis;

	public Camera(float fov, float aspect, float zNear, float zFar) {
		// Set the local variables
		this.fov = fov;
		this.aspect = aspect;
		this.zNear = zNear;
		this.zFar = zFar;

		// Create matrices
		projection = MatrixUtil.createPerspectiveProjection(fov, aspect, zNear, zFar);
		view = MatrixUtil.createIdentityMatrix();

		// Initialize position and rotation vectors
		position = new Vector3f(0, 0, 0);
		rotation = new Vector3f(0, 0, 0);

		// Create normalized axis vectors
		xAxis = new Vector3f(1, 0, 0);
		yAxis = new Vector3f(0, 1, 0);
		zAxis = new Vector3f(0, 0, 1);

		// Enable depth testing
		glEnable(GL_DEPTH_TEST);
	}

	public void apply() {
		// Make the view matrix an identity.
		view.setIdentity();

		// Rotate the view
		Matrix4f.rotate((float) Math.toRadians(rotation.x), xAxis, view, view);
		Matrix4f.rotate((float) Math.toRadians(rotation.y), yAxis, view, view);
		Matrix4f.rotate((float) Math.toRadians(rotation.z), zAxis, view, view);

		// Move the camera
		Matrix4f.translate(position, view, view);
	}

	public void move(float amount, float direction) {
		position.z += amount * Math.sin(Math.toRadians(rotation.y + 90 * direction));
		position.x += amount * Math.cos(Math.toRadians(rotation.y + 90 * direction));
		
		// + for free floating camera:
		if(direction == 1 /*strafe*/) { //no altitude change in case of strafe
			position.y -= amount * (Math.cos(Math.toRadians(rotation.x + 90)) + Math.cos(Math.toRadians(rotation.z + 90)));
		}
	}
	
	public void moveVertical(float amount, float direction) {
		position.y += amount * Math.sin(Math.toRadians(90 * direction));
	}
	
	public Matrix4f getViewMatrix() {
		return view;
	}

	public Matrix4f getProjectionMatrix() {
		return projection;
	}

	public void setPosition(Vector3f position) {
		this.position = position;
	}
	
	public Vector3f getPosition() {
		return position;
	}
	
	public Vector3f getRotarion() {
		return rotation;
	}
	
    /**
     * Add rotation (rx, ry, rz) to the camera
     */
    public void addRotation(float rx, float ry, float rz)
    {
        rotation.x += rx;
        rotation.y += ry;
        rotation.z += rz;
    }

	public void setRotation(int i, int j, int k) {
		this.rotation = new Vector3f(i, j, k);
	}
	
	public void setRotation(Vector3f rotation) {
		this.rotation = rotation;
	}

	public Matrix4f getNormalMatrix() {
		Matrix4f mat = getViewMatrix();
		mat.m30 = 0;
		mat.m31 = 0;
		mat.m32 = 0;
		mat.m33 = 1;
		Matrix4f.invert(mat, mat);
		Matrix4f.transpose(mat, mat);
		return mat;
	}
}
